﻿Public Class ViewModelTableEditor
    Inherits ViewModelTabSectionBase

    Friend Shared NumericTypes As List(Of Type) = _
        New List(Of Type)(New Type() {GetType(Double), GetType(Single), GetType(Int32), GetType(Int64)})

    Dim _SegmentationVariables As New ObjectModel.ObservableCollection(Of ViewModelTableEditorVariable)
    Dim _Filters As New ObjectModel.ObservableCollection(Of ViewModelTableEditorFilter)
    Dim _datasource As DataView
    Dim _EditionData As DataView

    Private _datasourcename As String
    Private _SegmentationVariableList As String
    Private _EditableVariableList As String

    Public Property DataSourceName As String
        Get
            Return _datasourcename
        End Get
        Set(value As String)
            _datasourcename = value
        End Set
    End Property

    Public Property SegmentationVariableList As String
        Get
            Return _SegmentationVariableList
        End Get
        Set(value As String)
            _SegmentationVariableList = value
        End Set
    End Property

    Public Property EditableVariableList As String
        Get
            Return _EditableVariableList
        End Get
        Set(value As String)
            _EditableVariableList = value
        End Set
    End Property

    Public Property DataSource As DataView
        Get
            Return _datasource
        End Get
        Set(value As DataView)
            _datasource = value
            _SegmentationVariables.Clear()

            Dim edittable As New DataTable

            Dim segcols As List(Of DataColumn)
            Dim varcols As List(Of DataColumn)

            If Not String.IsNullOrEmpty(SegmentationVariableList) Then
                segcols = (From str In SegmentationVariableList.Split(New String() {";"}, StringSplitOptions.RemoveEmptyEntries)
                           Select _datasource.Table.Columns(str)).ToList()

            Else
                segcols = (From col As DataColumn In _datasource.Table.Columns
                           Where Not NumericTypes.Contains(col.DataType)
                           Select col).ToList()
            End If

            If Not String.IsNullOrEmpty(EditableVariableList) Then
                varcols = (From str In EditableVariableList.Split(New String() {";"}, StringSplitOptions.RemoveEmptyEntries)
                           Select _datasource.Table.Columns(str)).ToList()

            Else
                varcols = (From col As DataColumn In _datasource.Table.Columns
                           Where NumericTypes.Contains(col.DataType)
                           Select col).ToList()
            End If

            For Each col In segcols
                _SegmentationVariables.Add(New ViewModelTableEditorVariable(Me, col.ColumnName))
            Next
            For Each col In varcols
                edittable.Columns.Add(col.ColumnName, col.DataType)
            Next

            If _datasource.Table.DataSet Is Nothing Then
                Dim dset = New DataSet
                dset.Tables.Add(_datasource.Table)
            End If

            _datasource.Table.DataSet.Tables.Add(edittable)

            Dim defaultrow = edittable.NewRow()
            For Each c As DataColumn In edittable.Columns
                defaultrow(c.ColumnName) = _datasource.Table.Compute("AVG(" & c.ColumnName & ")", "")
            Next

            edittable.Rows.Add(defaultrow)

            Filters.Clear()
            Filters.Add(New ViewModelTableEditorFilter(Me))

            EditionData = edittable.DefaultView


            OnPropertyChanged("DataSource")
        End Set
    End Property

    Public Property EditionData As DataView
        Get
            If _datasource Is Nothing And Not String.IsNullOrEmpty(_datasourcename) Then
                Dim cachemanager = New DataManager()
                DataSource = New DataView(cachemanager.Dataset.Tables(_datasourcename))
            End If
            Return _EditionData
        End Get
        Set(value As DataView)
            _EditionData = value
            OnPropertyChanged("EditionData")
        End Set
    End Property

    Public ReadOnly Property SegmentationVariables As ObjectModel.ObservableCollection(Of ViewModelTableEditorVariable)
        Get
            Return _SegmentationVariables
        End Get
    End Property

    Public ReadOnly Property Filters As ObjectModel.ObservableCollection(Of ViewModelTableEditorFilter)
        Get
            Return _Filters
        End Get
    End Property

    Public Property DeleteVariableCommand As UICommand

    Private Sub DeleteVariable(name As Object)
        Dim var = SegmentationVariables.Where(Function(v) v.Name = name).FirstOrDefault()

        If var IsNot Nothing Then
            var.UnUsed = True
        End If

        UpdateFiltering()
    End Sub

    Friend Function FiltersToString() As String
        If Filters.Count = 0 Then Return ""
        If Filters.Count = 1 Then Return Filters(0).ToString()

        Dim cleanfilters = Aggregate f In Filters Where String.IsNullOrEmpty(f.FilterString) Into Count()

        If cleanfilters > 0 Then Return ""

        Dim result = ""

        For i = 0 To Filters.Count - 1

            If i = (Filters.Count - 1) Then
                result = result & " (" & Filters(i).FilterString & ")"
            Else
                result = result & " (" & Filters(i).FilterString & ") " & Filters(i).FilterOperatorToString(Nothing)
            End If

        Next

        Return result
    End Function

    Friend Sub UpdateFiltering()
        Dim fstr = FiltersToString()

        If String.IsNullOrEmpty(fstr) Then Return

        _EditionData.RowFilter = fstr
    End Sub

    Sub New()
        testinstance = Me
        DeleteVariableCommand = New UICommand(AddressOf DeleteVariable)
    End Sub

    Friend Shared testinstance As ViewModelTableEditor
End Class

Public Class ViewModelTableEditorFilter
    Implements ComponentModel.INotifyPropertyChanged
    Implements Windows.Input.ICommand


    Dim _owner As ViewModelTableEditor
    Dim _variable As ViewModelTableEditorVariable
    Dim _operator As String
    Dim _filteroperator As String
    Dim _value As String

    Public Property Variable As ViewModelTableEditorVariable
        Get
            Return _variable

        End Get
        Set(value As ViewModelTableEditorVariable)
            _variable = value
            _owner.UpdateFiltering()
        End Set
    End Property

    Public Property [Operator] As String
        Get
            Return _operator
        End Get
        Set(value As String)
            _operator = value
            _owner.UpdateFiltering()

        End Set
    End Property

    Public Property [FilterOperator] As String
        Get
            Return _filteroperator
        End Get
        Set(value As String)
            _filteroperator = value
            _owner.UpdateFiltering()

        End Set
    End Property

    Public Property Value As String
        Get
            Return _value
        End Get
        Set(value As String)
            _value = value
            _owner.UpdateFiltering()

        End Set
    End Property

    Sub New(owner As ViewModelTableEditor)
        _owner = owner
    End Sub

    Public ReadOnly Property Variables As ObjectModel.ObservableCollection(Of ViewModelTableEditorVariable)
        Get
            Return _owner.SegmentationVariables
        End Get
    End Property

    Public ReadOnly Property Operators As ObjectModel.ObservableCollection(Of String)
        Get
            Return New ObjectModel.ObservableCollection(Of String)(New String() {"=EQUAL", "!=NOT EQUAL"})
        End Get
    End Property

    Public ReadOnly Property FilterOperators As ObjectModel.ObservableCollection(Of String)
        Get
            Return New ObjectModel.ObservableCollection(Of String)(New String() {" OU ", " ET "})
        End Get
    End Property

    Private Event PropertyChanged(sender As Object, e As ComponentModel.PropertyChangedEventArgs) Implements ComponentModel.INotifyPropertyChanged.PropertyChanged
    Private Event CanExecuteChanged(sender As Object, e As EventArgs) Implements Windows.Input.ICommand.CanExecuteChanged

    Private Function CanExecute(parameter As Object) As Boolean Implements Windows.Input.ICommand.CanExecute
        Return True
    End Function
    Private Sub Execute(parameter As Object) Implements Windows.Input.ICommand.Execute
        If parameter = "delete" Then
            _owner.Filters.Remove(Me)
        End If

        If parameter = "addnew" Then
            Dim newfilter = New ViewModelTableEditorFilter(_owner)
            newfilter.Variable = newfilter.Variables(0)
            newfilter.Operator = newfilter.Operators(0)
            newfilter.FilterOperator = newfilter.FilterOperators(0)
            _owner.Filters.Add(newfilter)
        End If
    End Sub

    Public Overrides Function ToString() As String
        If Variable Is Nothing Then Return ""
        If OperatorToString(Nothing) = "" Then Return ""
        Return Variable.Name & " " & OperatorToString(Nothing) & " '" & Value & "'"
    End Function

    Public ReadOnly Property FilterString As String
        Get
            Return ToString()
        End Get
    End Property

    Private Function OperatorToString([option] As Object) As String
        If [Operator] = "=EQUAL" Then Return "="
        If [Operator] = "!=NOT EQUAL" Then Return "<>"
        Return ""
    End Function
    Friend Function FilterOperatorToString([option] As Object) As String
        If [FilterOperator] = " OU " Then Return "OR"
        If [FilterOperator] = " ET " Then Return "AND"
        Return "AND"
    End Function
End Class

Public Class ViewModelTableEditorVariable
    Implements ComponentModel.INotifyPropertyChanged
    Implements Windows.Input.ICommand


    Dim _owner As ViewModelTableEditor
    Dim _unused As Boolean = True
    Dim _values As New ObjectModel.ObservableCollection(Of String)

    Public Property Name As String

    Public Property UnUsed As Boolean
        Get
            Return _unused
        End Get
        Set(value As Boolean)
            If value <> _unused Then
                _unused = value
                UpdateTables()
            Else
                _unused = value

            End If

            RaiseEvent PropertyChanged(Me, New ComponentModel.PropertyChangedEventArgs("UnUsed"))
        End Set
    End Property

    Sub New(owner As ViewModelTableEditor, name As String)
        _owner = owner
        Me.Name = name
        _unused = True

        For Each r As DataRow In _owner.DataSource.ToTable(True, name).Rows
            _values.Add(r(name).ToString())
        Next
    End Sub



    Public ReadOnly Property Values As ObjectModel.ObservableCollection(Of String)
        Get
            Return _values
        End Get
    End Property

    Private Event PropertyChanged(sender As Object, e As ComponentModel.PropertyChangedEventArgs) Implements ComponentModel.INotifyPropertyChanged.PropertyChanged

    Private Function CanExecute(parameter As Object) As Boolean Implements Windows.Input.ICommand.CanExecute
        Return True
    End Function

    Private Event CanExecuteChanged(sender As Object, e As EventArgs) Implements Windows.Input.ICommand.CanExecuteChanged

    Private Shared relnameinc As Integer = 1
    Private Shared tablenameinc As Integer = 1
    Private Sub Execute(parameter As Object) Implements Windows.Input.ICommand.Execute
        UnUsed = False
        _owner.UpdateFiltering()

    End Sub

    Friend Sub UpdateTables()
        Dim numericcols As List(Of DataColumn)

        If String.IsNullOrEmpty(_owner.EditableVariableList) Then
            numericcols = (From col As DataColumn In _owner.EditionData.Table.Columns
                           Where ViewModelTableEditor.NumericTypes.Contains(col.DataType)
                           Select col).ToList()
        Else
            numericcols = (From str In _owner.EditableVariableList.Split(New String() {";"}, StringSplitOptions.RemoveEmptyEntries)
                          Select _owner.EditionData.Table.Columns(str)).ToList()
        End If


        Dim segvars = From var In _owner.SegmentationVariables
                      Where Not var.UnUsed
                      Select var.Name

        Dim resulttable As DataTable = Nothing

        If segvars.Count() > 0 Then
            resulttable = _owner.DataSource.Table.DefaultView.ToTable(True, segvars.ToArray())
            tablenameinc = tablenameinc + 1
            resulttable.TableName = "TABLE_" & tablenameinc
            _owner.DataSource.Table.DataSet.Tables.Add(resulttable)

            Dim parentcols = (From col As DataColumn In resulttable.Columns Select col).ToArray()
            Dim childcols = (From var In segvars Select _owner.DataSource.Table.Columns(var)).ToArray()

            relnameinc = relnameinc + 1

            Dim newrel = _owner.DataSource.Table.DataSet.Relations.Add(parentcols, childcols)

            For Each col In numericcols
                resulttable.Columns.Add(col.ColumnName, col.DataType, "AVG(" & "Child" & "." & col.ColumnName & ")")
            Next

        Else
            resulttable = New DataTable()
            For Each col In numericcols
                resulttable.Columns.Add(col.ColumnName, col.DataType)
            Next
            _owner.DataSource.Table.DataSet.Tables.Add(resulttable)
            Dim defaultrow = resulttable.NewRow()
            For Each c As DataColumn In resulttable.Columns
                defaultrow(c.ColumnName) = _owner.DataSource.Table.Compute("AVG(" & c.ColumnName & ")", "")
            Next

            resulttable.Rows.Add(defaultrow)

        End If

        _owner.EditionData = resulttable.DefaultView

    End Sub
End Class

Public Class UICommand
    Implements Windows.Input.ICommand

    Dim method As Action(Of Object)

    Sub New(method As Action(Of Object))
        Me.method = method
    End Sub

    Private Function CanExecute(parameter As Object) As Boolean Implements Windows.Input.ICommand.CanExecute
        Return True
    End Function

    Private Event CanExecuteChanged(sender As Object, e As EventArgs) Implements Windows.Input.ICommand.CanExecuteChanged

    Private Sub Execute(parameter As Object) Implements Windows.Input.ICommand.Execute
        method.Invoke(parameter)
    End Sub
End Class

